﻿using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;


namespace Tanker.Test
{
    /// <summary>
    ///This is a test class for GenericSequence class.
    ///</summary>
    [TestClass()]
    public class GenericSequenceTest
    {

        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        #region Projecting Methods

        /// <summary>
        /// A test for Select
        /// </summary>
        [TestMethod()]
        public void SelectTest()
        {
            Sequence<int> intSeq = Sequence.Create(new int[] { 1, 2, 3, 4, 5 });
            Sequence<int> query = intSeq.Select<int>(delegate(int i) { return i + i; });
            Sequence<int> expectedSeq = Sequence.Create(new int[] { 2, 4, 6, 8, 10 });
            Assert.IsTrue(query.SequentialEqual(expectedSeq));
        }

        /// <summary>
        ///A test for Select
        ///</summary>
        [TestMethod()]
        public void SelectIndexTest()
        {
            Sequence<int> intSeq = Sequence.Create(new int[] { 1, 2, 3, 4, 5 });
            Sequence<int> query = intSeq.Select<int>(delegate(int e, int index) { return e + index; });
            Sequence<int> expectedSeq = Sequence.Create(new int[] { 1, 3, 5, 7, 9 });
            Assert.IsTrue(query.SequentialEqual(expectedSeq));

            Sequence<string> names = Sequence.Create(new string[] { "Mr.Han", "Mike", "Peijuin", "Elvin", "Pranalan" });
            Sequence<string> query2 = names.Select<string>(delegate(string e, int index) { return index.ToString() + "=" + e; });
            Sequence<string> expectedSeq2 = Sequence.Create(new string[] { "0=Mr.Han", "1=Mike", "2=Peijuin", "3=Elvin", "4=Pranalan" });
            Assert.IsTrue(query2.SequentialEqual(expectedSeq2));

        }

        /// <summary>
        ///A test for Zip
        ///</summary>
        [TestMethod()]
        public void ZipTest()
        {
            int[] numbers = { 1, 2, 3, 4 };
            string[] words = { "one", "two", "three" };

            Sequence<int> numSeq = Sequence.Create(numbers);
            Sequence<string> wordSeq = Sequence.Create(words);

            Sequence<string> zippedSeq = wordSeq.Zip<int, string>(numSeq, delegate(string x, int y) { return x + y.ToString(); });
            Sequence<string> expectedSeq = Sequence.Create(new string[] {"one1", "two2", "three3"});

            Assert.IsTrue(zippedSeq.Count() == 3);
            Assert.IsTrue(zippedSeq.SequentialEqual(expectedSeq));
        }

        #endregion

        #region Conversion Methods

        /// <summary>
        /// A test for ToArray()
        /// </summary>
        [TestMethod]
        public void ToArrayTest()
        {
            Sequence<int> intSeq = Sequence.Repeat(1, 10);
            int[] array = intSeq.ToArray();

            Assert.IsTrue(array.Length == 10);
        }

        /// <summary>
        /// A test for ToDictionary()
        /// </summary>
        [TestMethod]
        public void ToDictionary()
        {
            Sequence<int> intSeq = Sequence.Range(1, 10);
            Dictionary<int, long> dictionary = intSeq.ToDictionary<int, long>(delegate(int i) { return i; },
                                                                              delegate(int i) { return (long)i * i; });
            Assert.IsTrue(dictionary[10] == 100L);
        }

        #endregion

        #region Filtering Methods

        /// <summary>
        ///A test for Where
        ///</summary>
        [TestMethod()]
        public void WhereTest()
        {
            Sequence<int> intSeq = Sequence.Range(0, 10);
            Sequence<int> query = intSeq.Where(delegate(int i) { return i % 2 == 0; });
            Sequence<int> expectedSeq = Sequence.Create(new int[] { 0, 2, 4, 6, 8 });

            Assert.IsTrue(query.SequentialEqual(expectedSeq));
        }

        /// <summary>
        ///A test for Where
        ///</summary>
        [TestMethod()]
        public void WhereIndexTest()
        {
            Sequence<int> intSeq = Sequence.Range(0, 10);
            Sequence<int> query = intSeq.Where(delegate(int e, int index) { return e % 2 == 0 && index == 2; });
            Sequence<int> expectedSeq = Sequence.Create(new int[] { 2 });

            Assert.IsTrue(query.SequentialEqual(expectedSeq));
        }

        /// <summary>
        ///A test for Take
        ///</summary>
        [TestMethod()]
        public void TakeTest()
        {
            Sequence<int> sequence = Sequence.Repeat(0, 10);
            Sequence<int> tenElements = sequence.Take(10);

            Assert.IsTrue(tenElements.Count() == 10);

            Sequence<int> zeroElement = sequence.Take(0);
            Assert.IsTrue(zeroElement.Count() == 0);
        }

        /// <summary>
        ///A test for Take (int) - throws ArgumentException if count is lest than 0.
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentException))]
        public void TakeArguementExceptionTest()
        {
            Sequence<int> sequence = Sequence.Repeat(0, 10);
            Sequence<int> query1 = sequence.Take(-1);
        }

        /// <summary>
        ///A test for Take (int) - throws InvalidOperationException if count exceeds the number of elements.
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TakeInvalidOperationExceptionTest()
        {
            Sequence<int> sequence = Sequence.Repeat(0, 2);
            Sequence<int> query1 = sequence.Take(4);

            foreach (int i in query1) ;

        }

        /// <summary>
        ///A test for TakeWhile
        ///</summary>
        [TestMethod()]
        public void TakeWhileTest()
        {
            Sequence<int> sequence = Sequence.Create(new int[] { 1, 2, 3, 4, 3, 2, 1 });
            Sequence<int> query = sequence.TakeWhile(delegate(int i) { return i < 4; });

            Assert.IsTrue(query.Count() == 3);
        }

        /// <summary>
        /// A Test for Skip(int count)
        /// </summary>
        [TestMethod()]
        public void SkipTest()
        {
            Sequence<int> sequence = Sequence.Range(0, 10);
            Sequence<int> skipQuery = sequence.Skip(5);

            Assert.IsTrue(skipQuery.Count() == 5);
        }

        /// <summary>
        ///A test for Skip (int) - throws InvalidOperationException if count exceeds the number of elements.
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(InvalidOperationException))]
        public void SkipInvalidOperationExceptionTest()
        {
            Sequence<int> sequence = Sequence.Range(0, 10);
            Sequence<int> skipQuery = sequence.Skip(50);

            foreach (int i in skipQuery) ;
        }

        /// <summary>
        /// A test for SkipWhile 
        ///</summary>
        [TestMethod()]
        public void SkipWhileTest()
        {
            Sequence<int> ints = Sequence.Create(new int[] { 3, 5, 2, 234, 4, 1 });
            Sequence<int> query = ints.SkipWhile(delegate(int i) { return i < 100; });
            Sequence<int> expectedQuery = Sequence.Create(new int[] { 234, 4, 1 });

            Assert.IsTrue(query.Count() == 3);
            Assert.IsTrue(query.SequentialEqual(expectedQuery));
        }

        /// <summary>
        /// A test for Distinct method
        /// </summary>
        [TestMethod]
        public void DistinceTest()
        {
            Sequence<int?> ints = Sequence.Create(new int?[] { null, null, null });
            Sequence<int?> distinctNumbers = ints.Distinct();

            Assert.IsTrue(distinctNumbers.Count() == 1);
            Assert.IsNull(distinctNumbers.First());

            Sequence<int> nums = Sequence.Create(new int[] { 1, 1, 2, 2, 3, 3 });
            Sequence<int> uniqueNums = nums.Distinct();

            Assert.IsTrue(uniqueNums.Count() == 3);
        }

        #endregion

        #region Element Operators

        /// <summary>
        /// A test for First()
        /// </summary>
        [TestMethod]
        public void FirstTest()
        {
            Sequence<int> ints = Sequence.Range(0, 10);
            int first = ints.First();

            Assert.IsTrue(first == 0);
        }

        /// <summary>
        /// A test for First
        /// </summary>
        [TestMethod]
        public void FirstWithPredicateTest()
        {
            Sequence<int> ints = Sequence.Range(1, 10);
            int first = ints.First(delegate(int i) { return i % 4 == 0; });

            Assert.IsTrue(first == 4);
        }

        /// <summary>
        /// A test for First
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void FirstInvalidOperationExceptionTest()
        {
            Sequence<int> ints = Sequence.Range(1, 10);
            int first = ints.First(delegate(int i) { return i % 100 == 0; });
        }

        /// <summary>
        /// A test for Last()
        /// </summary>
        [TestMethod]
        public void LastTest()
        {
            List<int> intList = new List<int>();
            intList.AddRange(new int[] { 1, 2, 3, 4, 5 });
            Sequence<int> ints = Sequence.Create(intList);
            int last = ints.Last();

            Assert.IsTrue(last == 5);
        }

        /// <summary>
        /// A test for exception thrown from Last()
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void LastInvalidOperationExceptionTest()
        {
            Sequence<int> empty = Sequence.Empty<int>();
            empty.Last();
        }


        /// <summary>
        /// A test for Last
        /// </summary>
        [TestMethod]
        public void LastWithPredicateTest()
        {
            List<int> intList = new List<int>();
            intList.AddRange(new int[] { 1, 2, 3, 4, 5 });
            Sequence<int> ints = Sequence.Create(intList);
            int last = ints.Last(delegate(int i) { return i % 2 == 0; });

            Assert.IsTrue(last == 4);
        }

        /// <summary>
        /// A test for exception thrown from Last
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(InvalidOperationException))]
        public void LastWithPredicateInvalidOperationExceptionTest()
        {
            List<int> intList = new List<int>();
            intList.AddRange(new int[] { 1, 2, 3, 4, 5 });
            Sequence<int> ints = Sequence.Create(intList);
            int last = ints.Last(delegate(int i) { return i % 6 == 0; });
        }

        /// <summary>
        /// A test for LastOrDefault()
        /// </summary>
        [TestMethod]
        public void LastOrDefaultTest()
        {
            Sequence<int> ints = Sequence.Range(1, 10);
            int last = ints.LastOrDefault();
            Assert.IsTrue(last == 10);

            Sequence<int> empty = Sequence.Empty<int>();
            last = empty.LastOrDefault();
            Assert.IsTrue(last == default(int));
        }

        /// <summary>
        /// A test for LastOrDefault
        /// </summary>
        [TestMethod]
        public void LastOrDefaultWithPredicateTest()
        {
            Sequence<int> ints = Sequence.Range(1, 10);
            int last = ints.LastOrDefault(delegate(int i) { return i % 3 == 0; });
            Assert.IsTrue(last == 9);

            Sequence<int> empty = Sequence.Empty<int>();
            last = empty.LastOrDefault(delegate(int i) { return i % 3 == 0; });
            Assert.IsTrue(last == default(int));
        }

        /// <summary>
        /// A test for ElementAt()
        /// </summary>
        [TestMethod]
        public void ElementAtTest()
        {
            List<int> nums = new List<int>();
            nums.AddRange(new int[] { 1, 2, 3 });
            Sequence<int> sequence = Sequence.Create(nums);

            Assert.IsTrue(sequence.ElementAt(0) == 1);
            Assert.IsTrue(sequence.ElementAt(1) == 2);
            Assert.IsTrue(sequence.ElementAt(2) == 3);
        }

        /// <summary>
        /// A test for exception thrown from ElementAt() if the index is less than 0
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ElementAtThrowsArgumentExceptionIfIndexIsLessThanZero()
        {
            List<int> nums = new List<int>();
            nums.AddRange(new int[] { 1, 2, 3 });
            Sequence<int> sequence = Sequence.Create(nums);

            sequence.ElementAt(-1);
        }

        /// <summary>
        /// A test for exception thrown from ElementAt() if not enough elements
        /// </summary>
        [TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public void ElementAtThrowsArgumentExceptionIfNotEnoughElements()
        {
            List<int> nums = new List<int>();
            nums.AddRange(new int[] { 1, 2, 3 });
            Sequence<int> sequence = Sequence.Create(nums);

            sequence.ElementAt(4);
        }

        #endregion

        #region Quantifiers
        /// <summary>
        /// A test for SequentialEqual()
        /// </summary>
        [TestMethod]
        public void SequentialEqualTest()
        {
            Sequence<int> first = Sequence.Range(1, 10);
            Sequence<int> second = Sequence.Range(1, 10);

            Assert.IsTrue(first.SequentialEqual(second));
        }

        #endregion

        #region Ordering
        /// <summary>
        /// A test for Reverse()
        /// </summary>
        [TestMethod]
        public void ReverseTest()
        {
            Sequence<int> ints = Sequence.Range(1, 100000);
            Sequence<int> reverse = ints.Reverse();

            Assert.IsTrue(reverse.First() == 100000);
        }

        #endregion

        #region Set Operators

        /// <summary>
        /// A test for Concat Method
        /// </summary>
        [TestMethod]
        public void ConcatTest()
        {
            Sequence<int> first = Sequence.Range(1, 10);
            Sequence<int> second = Sequence.Range(11, 10);

            Sequence<int> both = first.Concat(second);

            Assert.IsTrue(both.Count() == 20);
        }

        /// <summary>
        /// A test for Union Method
        /// </summary>
        [TestMethod]
        public void UnionTest()
        {
            Sequence<int> first = Sequence.Range(1, 10);
            Sequence<int> second = Sequence.Range(1, 10);

            Sequence<int> union = first.Union(second);

            Assert.IsTrue(union.Count()==10);
        }

        /// <summary>
        /// A test for Intersect Method
        /// </summary>
        [TestMethod]
        public void IntersectTest()
        {
            Sequence<int> first = Sequence.Range(1, 10);
            Sequence<int> second = Sequence.Range(1, 10);
            Sequence<int> intersectSeq = first.Intersect(second);
            Assert.IsTrue(intersectSeq.SequentialEqual(second));

            first = Sequence.Create(new int[] {1, 2, 3});
            second = Sequence.Create(new int[] { 11, 22, 33 });
            intersectSeq = first.Intersect(second);
            Assert.IsTrue(intersectSeq.IsEmpty());
        }

        /// <summary>
        /// A test for Except Method
        /// </summary>
        [TestMethod]
        public void ExceptTest()
        {
            Sequence<int> first = Sequence.Range(1, 10);
            Sequence<int> second = Sequence.Range(1, 10);
            Sequence<int> intersectSeq = first.Except(second);
            Assert.IsTrue(intersectSeq.IsEmpty());

            first = Sequence.Create(new int[] { 1, 2, 3 });
            second = Sequence.Create(new int[] { 11, 22, 33 });
            intersectSeq = first.Except(second);
            Assert.IsTrue(intersectSeq.SequentialEqual(first));
        }

        #endregion
    }
}
